package cn.xeblog.plugin.tools.browser.core;

import cn.hutool.core.util.StrUtil;
import com.intellij.ui.jcef.JBCefBrowser;
import org.cef.CefClient;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.handler.*;
import org.cef.misc.BoolRef;
import org.cef.network.CefRequest;

import java.awt.*;

/**
 * jcef浏览器服务
 *
 * @author anlingyi
 * @date 2022/8/15 2:06 PM
 * @see <a href="https://plugins.jetbrains.com/docs/intellij/jcef.html#jbcefbrowser">jcef浏览器</a>
 * @see <a href="http://www.xuanyimao.com/jcef/doc/overview-summary.html">api</a>
 */
public class JcefBrowserService implements BrowserService {

  /**
   * JBCef浏览器
   */
  private final JBCefBrowser jbCefBrowser;

  /**
   * cefBrowser用于
   */
  private final CefBrowser cefBrowser;

  /**
   * 客户端
   */
  private final CefClient client;

  /**
   * 用户代理
   */
  private UserAgent userAgent;

  /**
   * 事件监听
   */
  private BrowserEventListener eventListener;

  public JcefBrowserService(String url) {
    // 初始化监听
    this.jbCefBrowser = new JBCefBrowser(url);
    this.cefBrowser = this.jbCefBrowser.getCefBrowser();
    this.client = this.cefBrowser.getClient();
    // 默认是使用手机版浏览器
    this.userAgent = UserAgent.IPHONE;
    this.eventListener = new BrowserEventListener() {
    };
    // 添加事件
    initAddEvent();
  }

  private void initAddEvent() {
    // 先移除请求处理
    this.client.removeRequestHandler();
    // 添加以处理与浏览器请求相关的事件
    this.client.addRequestHandler(new CefRequestHandlerAdapter() {
      @Override
      public CefResourceRequestHandler getResourceRequestHandler(CefBrowser browser, CefFrame frame, CefRequest request,
          boolean isNavigation, boolean isDownload, String requestInitiator,
          BoolRef disableDefaultHandling) { // 在一个资源请求初始化前在CEF IO线程上调用

        return new CefResourceRequestHandlerAdapter() {
          @Override
          public boolean onBeforeResourceLoad(CefBrowser browser, CefFrame frame, CefRequest request) { // 在资源加载前
            // 设置用户代理
            String ua = userAgent.getValue();
            if (ua != null) {
              request.setHeaderByName("User-Agent", ua, true);
            }
            return false;
          }
        };
      }
    });

    // 添加以处理与浏览器显示状态相关的事件
    this.client.addDisplayHandler(new CefDisplayHandlerAdapter() {
      @Override
      public void onAddressChange(CefBrowser browser, CefFrame frame, String url) { // 浏览器地址变更监听
        // 如果不是以http开头,跳过
        if (!StrUtil.startWith(url, "http")) {
          return;
        }

        eventListener.onAddressChange(url);
      }
    });

    // 移除以处理与浏览器生命周期相关的事件
    this.client.removeLifeSpanHandler();
    // 添加以处理与浏览器生命周期相关的事件
    this.client.addLifeSpanHandler(new CefLifeSpanHandlerAdapter() {
      @Override
      public boolean onBeforePopup(CefBrowser browser, CefFrame frame, String target_url, String target_frame_name) { // 在创建新的弹出窗口之前调用IO线程
        // 如果目标地址以指定后缀结束,跳过
        if (StrUtil.endWithAnyIgnoreCase(target_url, "jpg", "png", "gif", "svg", "pdf", "bmp", "webp")) {
          return false;
        }

        // 加载目标地址
        loadURL(target_url);
        return true;
      }

      @Override
      public void onBeforeClose(CefBrowser browser) { // 在浏览器被销毁之前调用
        close();
        // 浏览器关闭之前的监听
        eventListener.onBeforeClose();
      }
    });
  }

  @Override
  public Component getUI() {
    return this.jbCefBrowser.getComponent();
  }

  @Override
  public void loadURL(String url) {
    this.jbCefBrowser.loadURL(url);
  }

  @Override
  public void goBack() {
    if (this.cefBrowser.canGoBack()) {
      this.cefBrowser.goBack();
    }
  }

  @Override
  public void goForward() {
    if (this.cefBrowser.canGoForward()) {
      this.cefBrowser.goForward();
    }
  }

  @Override
  public void reload() {
    this.cefBrowser.reload();
  }

  @Override
  public void setZoomLevel(double value) {
    this.cefBrowser.setZoomLevel(value);
  }

  @Override
  public void close() {
    // 释放Window及其子组件及其所有子组件使用的所有本机屏幕资源
    this.client.dispose();
    this.jbCefBrowser.dispose();
  }

  @Override
  public void setUserAgent(UserAgent userAgent) {
    if (userAgent == null) {
      return;
    }

    this.userAgent = userAgent;
  }

  @Override
  public void addEventListener(BrowserEventListener listener) {
    if (listener == null) {
      return;
    }

    this.eventListener = listener;
  }

}
